#ifndef __HTTP_HPP__
#define __HTTP_HPP__

#include <cassert>// 此头文件可能重复包含

#include "Utils.hpp"
// #include "../src/Server.hpp"
#include "../src/Log.hpp"
#include "../src/Buffer.hpp"
#include <regex>

#define Test 0
#if Test
#include <cassert>
#include "../src/Buffer.hpp"
#endif

#define HTTP_LINE_MAX_LENGTH 1024 * 8   // HTTP请求中的一行数据最大长度
#define HTTP_SERVER_DEFAULT_TIMEOUT 10  // HTTP服务器上连接的默认超时时间
#define HTTP_DEFAULT_VERSION "HTTP/1.1" // HTTP协议默认版本
// #define HTTP_STATIC_RESOURCE_ROOT_PATH "www" // HTTP服务器上的静态资源根目录

/**
 * @brief HTTP请求的结构化数据的数据结构定义

 */
class HttpRequest
{
public:/* 设置成公有属性，方便访问 */
    std::string _method;                                    // 请求方法
    std::string _path;                                      // 资源路径
    std::unordered_map<std::string, std::string> _params;   // 查询字符串
    std::string _version;                                   // HTTP版本
    std::unordered_map<std::string, std::string> _headers;  // 请求头部
    std::string _body;                                      // 请求正文
    
    std::smatch _matchers;                                  // 保存通过正则库解析首行得到的数据。解析HttpRequest时用
public:
    HttpRequest(): _version(HTTP_DEFAULT_VERSION)
    {}
public:
    /**
     * @brief 设置请求头部字段。存在就修改
     * 
     * @param key 头部字段键码
     * @param val 头部字段键值
     */
    void SetHeader(const std::string& key, const std::string& val)
    {
        _headers[key] = val;
    }
    /**
     * @brief 判断是否存在指定键码的头部字段
     * 
     * @param key 要查询的键码
     * @return true 存在；\
     * @return false 不存在
     */
    bool HasHeader(const std::string& key) const
    {
        return _headers.count(key) > 0;
    }
    /**
     * @brief 根据键码，获取指定头部字段的值。
     * 
     * @param key 要获取的头部字段的键码
     * @return std::string 该头部字段的键值；\
     * @return 不存在就返回空串
     */
    std::string GetHeader(const std::string& key) const
    {
        auto it = _headers.find(key);
        if(it != _headers.end()) return it->second;
        else return "";
    }
    /**
     * @brief 设置查询字符串。存在就修改
     * 
     * @param key 要设置的 查询字符串 的键码
     * @param val 要设置的 查询字符串 的键值
     */
    void SetParam(const std::string& key, const std::string& val)
    {
        _params[key] = val;
    }
    /**
     * @brief 判断是否存在指定键码的 查询字符串
     * 
     * @param key 查询的键码
     * @return true 存在；\
     * @return false 不存在
     */
    bool HasParam(const std::string& key) const
    {
        return _params.count(key) > 0;
    }
    /**
     * @brief 根据键码，获取指定 查询字符串 的键值
     * 
     * @param key 要获取的 查询字符串 的键码
     * @return std::string 要获取的 查询字符串 的键值；\
     * @return 不存在就返回空串
     */
    std::string GetParam(const std::string& key) const
    {
        auto it = _params.find(key);
        if(it != _params.end()) return it->second;
        else return "";
    }

    /**
     * @brief 追加请求正文部分
     * 
     * @param body 正文 字符串
     */
    void AppendContent(const std::string& part)
    {
        _body += part;
    }
    /**
     * @brief 追加请求正文部分
     * 
     * @param start 正文首地址
     * @param len 正文长度
     */
    void AppendContent(const char* start, size_t len)
    {
        _body.append(start, len);
    }

    /**
     * @brief 获取该HTTP请求的正文的长度
     * 
     * @return size_t 正文长度
     */
    size_t ContentLength() const
    {
        if(HasHeader("Content-Length")) return std::stoul(GetHeader("Content-Length"));

        return 0;
    }
    /**
     * @brief 判断本次HTTP通信结束后，是否需要关闭连接
     * 
     * @return true 需要关闭。是短连接；
     * @return false 不需要关闭。是长连接
     */
    bool ToClose() const
    {
        if(HasHeader("Connection") == false || GetHeader("Connection") == "close") return true;
        return false;// Connection: keep-alive
    }

    /**
     * @brief 重置HTTP请求的结构化数据对象
     * 
     */
    void Reset()
    {
        _method.clear();
        _path.clear();
        _params.clear();
        _version = HTTP_DEFAULT_VERSION;
        _headers.clear();
        _body.clear();

        std::smatch tmpSmatch;
        _matchers.swap(tmpSmatch);
    }
};

/**
 * @brief HTTP响应的结构化数据的数据结构定义
 * 
 */
class HttpResponse
{
public:
    int _statusCode;                                        // 响应状态码
    std::unordered_map<std::string, std::string> _headers;  // 响应头部字段
    std::string _body;                                      // 响应正文
    bool _redirect;                                         // 是否重定向
    std::string _redirectPath;                              // 重定向的目标资源路径
public:
    HttpResponse(): _statusCode(200), _redirect(false)
    {}
public:
    /**
     * @brief 设置响应头部字段。存在就修改
     * 
     * @param key 头部字段键码
     * @param val 头部字段键值
     */
    void SetHeader(const std::string& key, const std::string& val)
    {
        _headers[key] = val;
    }
    /**
     * @brief 判断是否存在指定键码的头部字段
     * 
     * @param key 要查询的键码
     * @return true 存在；\
     * @return false 不存在
     */
    bool HasHeader(const std::string& key) const
    {
        return _headers.count(key) > 0;
    }
    /**
     * @brief 根据键码，获取指定头部字段的值。
     * 
     * @param key 要获取的头部字段的键码
     * @return std::string 该头部字段的键值；\
     * @return 不存在就返回空串
     */
    std::string GetHeader(const std::string& key) const
    {
        auto it = _headers.find(key);
        if(it != _headers.end()) return it->second;
        else return "";
    }

    /**
     * @brief 设置响应正文和响应文件类型
     * 
     * @param body 要设置的响应正文
     * @param type 要设置的响应文件类型，默认是"text/html"
     */
    void SetContent(const std::string& body, const std::string& type = "text/html")
    {
        _body = body;
        SetHeader("Content-Type", type);
    }
    /**
     * @brief 设置重定向信息
     * 
     * @param objPath 要重定向到的目标资源路径
     */
    void SetRedirect(const std::string& objPath)
    {
        _redirect = true;
        _redirectPath = objPath;
    }
    /**
     * @brief 判断本次HTTP通信结束后，是否需要关闭连接
     * 
     * @return true 需要关闭。是短连接；
     * @return false 不需要关闭。是长连接
     */
    bool ToClose() const
    {
        if(HasHeader("Connection") && GetHeader("Connection") == "keep-alive") return false;
        return true;
    }

    /**
     * @brief 重置HTTP响应的结构化数据对象
     * 
     */
    void Reset()
    {
        _statusCode = 200;
        _headers.clear();
        _body.clear();
        _redirect = false;
        _redirectPath.clear();
    }
};


/**
 * @brief HTTP请求的接收状态
 * 
 */
typedef enum
{
    RECV_HTTP_LINE,        // 接收并处理请求行阶段
    RECV_HTTP_HEAD,        // 接收并处理请求头部阶段
    RECV_HTTP_BODY,        // 接收并处理请求正文阶段
    RECV_HTTP_SUCCESS,     // 接收并处理请求完成阶段
    RECV_HTTP_ERROR        // 接收并处理请求失败阶段
}HttpRecvSatus;

/**
 * @brief 记录HTTP请求数据的接收和处理进度。
 * 在一次HTTP请求数据接收时，可能这一次接收到的不是一个完整的HTTP请求数据，就无法对接收到的数据进行解析处理。
 * 需要记录接收和处理状态等信息，等待后续(可能多次接收)接收到完整的HTTP请求数据，再根据上下文记录的相关状态信息继续解析数据，
 * 最终得到一个完整的HTTP请求结构化数据对象
 * 
 */
class HttpContext
{
private:
    HttpRecvSatus _recvStatus;      // HTTP请求的接收状态。表示当前在接收的哪个阶段
    int _respStatusCode;            // 响应状态码
    HttpRequest _request;           // 接收并处理完成的HTTP请求结构化数据
public:
    HttpContext(): _recvStatus(RECV_HTTP_LINE), _respStatusCode(200)
    {}
private:
    /**
     * @brief 接收并解析请求行
     * 
     * @param buffer 输入输出型参数 从网络中得到的HTTP请求的序列化数据，存放在用户缓冲区
     * @return true 接收并解析成功；\
     * @return false 失败。接收失败设置响应状态码为414；解析失败设置响应状态码为400
     */
    bool RecvHttpLine(Buffer* buffer)/* buffer设置成指针类型是为了和OnMessage适配 */
    {
        /* 保证在接收处理请求行阶段 */
        if(_recvStatus != RECV_HTTP_LINE) return false;

        /* 获取请求行 */
        std::string httpLine = buffer->GetLine();
        // 不足一行
        if(httpLine.size() == 0)
        {
            // 缓冲区中的一行数据太长
            if(buffer->ReadAbleSize() > HTTP_LINE_MAX_LENGTH)
            {
                _recvStatus = RECV_HTTP_ERROR;
                _respStatusCode = 414;// URI Too Long
                ERR_LOG("HTTP REQUEST LINE RECV FAILED: %d", _respStatusCode);
                return false;
            }

            // 继续等待接收数据
            return true;
        }

        // 接收到的一行数据太长
        if(httpLine.size() > HTTP_LINE_MAX_LENGTH)
        {
            _recvStatus = RECV_HTTP_ERROR;
            _respStatusCode = 414;// URI Too Long
            ERR_LOG("HTTP REQUEST LINE RECV FAILED: %d", _respStatusCode);
            return false;
        }

        /* 修正buffer的可读偏移 */
        buffer->MoveReadOffset(httpLine.size());
        // DBG_LOG("INBUFFER SIZE: %ld", buffer->ReadAbleSize());
        // std::string tmp;
        // buffer->ReadAsString(tmp, buffer->ReadAbleSize());
        // DBG_LOG("INBUFFER: %s", tmp.c_str());

        /* 解析请求行 */
        bool ret =  ParseHttpLine(httpLine);
        if(ret == false)
        {
            return false;
        }

        /* 进入请求头部的接受处理阶段 */
        _recvStatus = RECV_HTTP_HEAD;

        return true;
    }
    /**
     * @brief 解析请求行
     * 
     * @param line 要解析的请求行序列化数据，存放在用户缓冲区
     * @return true 解析成功；\
     * @return false 解析失败。设置响应状态码为400
     */
    bool ParseHttpLine(std::string& line)
    {
        /* 保证在接收处理请求行阶段 */
        if(_recvStatus != RECV_HTTP_LINE) return false;

        // DBG_LOG("LINE: %s", line.c_str());

        /* GET /www/index.html?name=xxx&passwd=xxx HTTP/1.1 */
        std::smatch matches;// 保存 通过正则库规则解析得到的结果 的容器
        std::regex rgx("(GET|HEAD|POST|PUT|DELETE) ([^?]*)(?:\\?(.*))? (HTTP/1\\.[01])(?:\n|\r\n)?", std::regex::icase/* 忽略大小写 */);// 规则
        /*
            (GET|HEAD|POST|PUT|DELETE)：匹配并提取其中任意一个字符串，直到遇到空格
            ([^?]*)：[^?]匹配非'?'字符，*表示匹配0次或多次，()提取
            \\?(.*) ：\\?表示'?'字符，(.*)表示提取任意字符，即匹配并提取'?'之后的任意字符，直到遇到空格
            (HTTP/1\\.[01])：[01]表示匹配0或1，\\.表示'.'，(HTTP/1\\.[01])表示匹配并提取以'HTTP/1.'开头且以0或1结尾的字符串
            (?:\n|\r\n)?：(?:字符串)表示匹配指定格式字符串但不提取，后面的'?'表示匹配前面的表达式0次或1次
            (?:\\?(.*))?：与上同理
        */
        bool ret = std::regex_match(line, matches, rgx);
        if(ret == false)
        {
            _recvStatus = RECV_HTTP_ERROR;
            _respStatusCode = 400;// Bad Request
            ERR_LOG("HTTP REQUEST LINE PARSE FAILED: %d", _respStatusCode);
            return false;
        }

        /*
            0: GET /path/login?username=xxx&passwd=xxx HTTP/1.1

            1: GET
            2: /path/login
            3: username=xxx&passwd=xxx
            4: HTTP/1.1
        */
        _request._method = matches[1];
        std::transform(_request._method.begin(), _request._method.end(), _request._method.begin(), ::toupper/*C语言接口 使用全局作用域中的*/);// 将请求方法都转为大写
        // _request._method = matches[1];
        _request._path = Utils::UrlDecode(matches[2]);// 不需要将+转换为空格
        _request._version = matches[4];
        
        std::vector<std::string> params;
        Utils::Split(matches[3], "&", &params);
        for(auto& param : params)
        {
            size_t pos = param.find('=');
            if(pos == std::string::npos)
            {
                _recvStatus = RECV_HTTP_ERROR;
                _respStatusCode = 400;// Bad Request
                ERR_LOG("HTTP REQUEST LINE PARSE FAILED: %d", _respStatusCode);
                return false;
            }

            std::string key = Utils::UrlDecode(param.substr(0, pos), true);
            std::string val = Utils::UrlDecode(param.substr(pos + 1), true);
            _request.SetParam(key, val);
        }

        return true;
    }

    /**
     * @brief 接收并解析请求头部
     * 
     * @param buffer 输入输出型参数 从网络中得到的HTTP请求的序列化数据，存放在用户缓冲区
     * @return true 接收并解析成功；\
     * @return false 失败。接收失败设置响应状态码为414；解析失败设置响应状态码为400
     */
    bool RecvHttpHeader(Buffer* buffer)/* buffer设置成指针类型是为了和OnMessage适配 */
    {
        /* 保证在 请求报头 的接收处理阶段 */
        if(_recvStatus != RECV_HTTP_HEAD) return false;
        // DBG_LOG("START RECVPARSE HEADER");
        while(true)
        {
            // DBG_LOG("buffer size: %ld", buffer->ReadAbleSize());
            /* 获取一行数据，即头部的一个字段 */
            std::string oneLineOfHead = buffer->GetLine();

            /* 未读取到完整的一行数据 */
            if(oneLineOfHead.size() == 0)
            {
                // 一行数据过长
                if(buffer->ReadAbleSize() > HTTP_LINE_MAX_LENGTH)
                {
                    _recvStatus = RECV_HTTP_ERROR;
                    _respStatusCode = 414;// URI Too Long
                    ERR_LOG("HTTP REQUEST HEAD RECV FAILED: %d", _respStatusCode);
                    return false;
                }

                // 继续等待完整的一行数据
                return true;
            }

            /* 读取到完整的一行数据，但一行数据过长 */
            if(oneLineOfHead.size() > HTTP_LINE_MAX_LENGTH)
            {
                _recvStatus = RECV_HTTP_ERROR;
                _respStatusCode = 414;// URI Too Long
                ERR_LOG("HTTP REQUEST HEAD RECV FAILED: %d", _respStatusCode);
                return false;
            }

            /* 读取到空行，头部已完成接收处理，跳出循环 */
            if(oneLineOfHead == "\n" || oneLineOfHead == "\r\n")
            {
                // DBG_LOG("buffer size: %ld and content: [%s]", buffer->ReadAbleSize(), buffer->ReadPosition());
                buffer->MoveReadOffset(oneLineOfHead.size());
                break;
            }

            // DBG_LOG("parse before, LINE: %ld [%s]", oneLineOfHead.size(), oneLineOfHead.c_str());

            /* 解析头部字段 */
            int length = oneLineOfHead.size();// 修正bug：ParseHttpHeader中会删除oneLineOfHead末尾的回车换行符，所以要将长度先记录下来
            bool ret = ParseHttpHeader(oneLineOfHead);
            // if(ret) buffer->MoveReadOffset(oneLineOfHead.size());// bug
            if(ret) buffer->MoveReadOffset(length);// 修正后
            else 
            {
                ERR_LOG("HTTP REQUEST EHAD PARSE FAILED: %d", _respStatusCode);
                return false;
            }
            // DBG_LOG("LINE: %ld [%s]", oneLineOfHead.size(), oneLineOfHead.c_str());
        }
        // DBG_LOG("FINISH RECVPARSE HEAD");
        /* 进入HTTP请求正文的接收处理阶段 */
        _recvStatus = RECV_HTTP_BODY;

        return true;
    }
    /**
     * @brief 解析HTTP请求头部的一个(序列化)字段
     * 
     * @param headParam 要解析的头部字段
     * @return true 解析成功；\
     * @return false 失败。设置响应状态码为400
     */
    bool ParseHttpHeader(std::string& headParam)
    {
        /* 保证在 请求报头 的接收处理阶段 */
        if(_recvStatus != RECV_HTTP_HEAD) return false;

        /* 修正字段，去除末尾的\r\n */
        if(headParam.back() == '\n') headParam.pop_back();
        if(headParam.back() == '\r') headParam.pop_back();

        /* 格式错误 */
        auto pos = headParam.find(": ");
        if(pos == std::string::npos)
        {
            _recvStatus = RECV_HTTP_ERROR;
            _respStatusCode = 400;
            return false;
        }

        std::string key = headParam.substr(0, pos);
        std::string val = headParam.substr(pos + 2);
        _request.SetHeader(key, val);

        return true;
    }
    /**
     * @brief 接收HTTP请求正文。
     * 
     * @param buffer 输入输出型参数 从网络中得到的HTTP请求的序列化数据，存放在用户缓冲区
     * @return true 接收成功
     * @return false 接收失败
     */
    bool RecvHttpBody(Buffer* buffer)/* buffer设置成指针类型是为了和OnMessage适配 */
    {
        /* 保证在HTTP请求正文 的接收处理阶段 */
        if(_recvStatus != RECV_HTTP_BODY) return false;

        /* 获取HTTP请求正文的长度 */
        int contentLength = _request.ContentLength();
        if(contentLength == 0)
        {
            _recvStatus = RECV_HTTP_SUCCESS;
            return true;
        }

        int needContentLength = contentLength - _request._body.size();// 仍需要接收的正文部分的长度
        /* 追加正文部分 */

        if(buffer->ReadAbleSize() >= needContentLength)
        {
#if 0 /* 错误写法：正文可能非常长，超出string最大容量限制 */
            std::string tmp;
            buffer->ReadAsStringAndPop(tmp, needContentLength);
            _request.AppendContent(tmp);
#endif
            _request.AppendContent(buffer->ReadPosition(), needContentLength);
            buffer->MoveReadOffset(needContentLength);
            _recvStatus = RECV_HTTP_SUCCESS;
            _respStatusCode = 200;
            return true;
        }

        /* 请求依然不完整，直接接收缓冲区中的数据 */
#if 0
        std::string tmp;
        buffer->ReadAsStringAndPop(tmp, buffer->ReadAbleSize());
        _request.AppendContent(tmp);
#endif
        _request.AppendContent(buffer->ReadPosition(), buffer->ReadAbleSize());
        buffer->MoveReadOffset(buffer->ReadAbleSize());
        return true;
    }
public:
    /**
     * @brief 获取HTTP请求的接收状态。接收任务进行到哪一步了
     * 
     * @return HttpRecvSatus HTTP请求的接收状态码
     */
    HttpRecvSatus RecvStatus() const
    {
        return _recvStatus;
    }
    /**
     * @brief 获取HTTP请求的接收的结果
     * 
     * @return int 响应状态码
     */
    int RespStatusCode() const
    {
        return _respStatusCode;
    }
    /**
     * @brief 获取接收并解析完成的HTTP请求的结构化数据对象
     * 
     * @return HttpRequest HttpRequest对象
     */
    HttpRequest& Get()
    {
        return _request;
    }
    /**
     * @brief 接收并处理HTTP请求数据
     * 
     * @param buffer 输入输出型参数 HTTP请求所在的缓冲区
     */
    void RecvHttpRequest(Buffer* buffer)
    {
        switch (_recvStatus)
        {
        case RECV_HTTP_LINE:
            RecvHttpLine(buffer);
        case RECV_HTTP_HEAD:
            RecvHttpHeader(buffer);
        case RECV_HTTP_BODY:
            RecvHttpBody(buffer);
        default:
            break;
        }
    }

    /**
     * @brief 重置上下文
     * 
     */
    void Reset()
    {
        _recvStatus = RECV_HTTP_LINE;
        _respStatusCode = 200;
        _request.Reset();
    }
};

#endif